# coding=utf-8
# author=dn_小毅(qq:162054033)
# 动脑学院pythonVIP课程
# 创建于: 2018/5/1 21:05

# 一、简答题

# 1. 列表list_1 = [[1,2,3,4,5],[{“name”:”张三”,”age”:28},{“name”:”James”:”age:30}]] ,获取James的年龄
# list_1 = [[1,2,3,4,5],[{"name":"张三","age":28},{"name":"James","age":30}]]
# james_age = list_1[1][1]['age']
# print(james_age)

# 2. 名词解释: 可迭代对象、迭代器、生成器、装饰器
# 可迭代对象：它是容器数据类型，可遍历（for...in...）,字符串、列表、元组、字典都是可迭代对象。
# from collections import Iterable

# 迭代器：它是一个带状态的对象，他能在你调用next()方法的时候返回容器中的下一个值，任何实现了__iter__和__next__()方法的对象都是迭代器，
# __iter__返回迭代器自身，__next__返回容器中的下一个值，如果容器中没有更多元素了，则抛出StopIteration异常。

# 生成器：其实是一种特殊的迭代器，不过这种迭代器更加优雅。它不需要再像上面的类一样写__iter__()和__next__()方法了，只需要一个yiled关键字。

# 装饰器: 它本质上是一个Python函数，它可以让其它函数在不需要做任何代码变动的前提下增加额外功能，装饰器的返回值也是一个函数对象。

# 3. 名词解释：面向对象编程中“类”中有几种方法？分别解释各种方法的用途。
# 实例方法 只能通过实例名.方法名调用。可以访问类属性、实例属性，类方法、实例方法、静态方法。

# 类方法 可以通过实例名.方法名，也可以类名.方法名调用。 其不能访问实例属性和实例方法。

# 静态方法 和类方法很相似，不同的时候定义时要定义参数，可以通过实例名.方法名，也可以类名.方法名，其不能访问实例属性和实例方法。

# 4. 输出以下代码运行结果，解释变量作用域LEGB.
# x = 2
# z = 3
# def func_outer():    #1.先从外部执行
#     y = 9            # y=9
#     x = 0            # x=0
#     global z         # z为全局变量 z=3
#     def func_inner():  #6.执行内部函数
#         nonlocal x     #7.声明x为外侧嵌套变量
#         x += 5       #8.(x,y,z)=(2,9,6)  x=2+5=7
#         z = 6        #9.z为全局变量 z=6
#         print("func_inner",max(x, z),max(x, y)) #10.最后输出 x=7>z=6 输出7，x=7<y=9 输出9
#     z += 3          #2.在执行此行  3+3=6
#     x += 2          #3.在执行此行  0+2=2  x为func_outer局部变量
#     print(x,y,z)    #4.打印 2 9 6  x=0+2  y=9（局部变量） z=3+3（全局变量）
#     return func_inner()   #5.执行此行
# func_outer()

# 5. 解释什么是GIL(全局解释器锁)
# python中有全局解释器锁，由于全局解释器锁的存在，所以在同一时间内，python解释器只能运行一个线程的代码，这大大影响了python多线程的性能。
# python GIL 之所以会影响多线程等性能，是应为在多线程的情况下，只有当线程获得了一个全局锁的时候，那么该线程的代码才能运行，而全局锁只有一个，
# 所以使用python多线程，在同一时刻也只有一个线程在运行，因此在即使在多核的情况下也只能发挥出单核的性能。
# 对于有io操作的线程，当一个线程在做io操作的时候，因为io操作不需要cpu，所以，这个时候，python会释放python全局锁，这样其他需要运行的线程就会使用该锁。
# 对于cpu密集型的线程，比如一个线程可能一直需要使用cpu做计算，那么python中会有一个执行指令的计数器，当一个线程执行了一定数量的指令时，该线程就会停止执行并让出当前的锁，这样其他的线程就可以执行代码了。
# 由上面可知，至少有两种情况python会做线程切换，一是一但有IO操作时，会有线程切换，二是当一个线程连续执行了一定数量的指令时，会出现线程切换。当然此处的线程切换不一定就一定会切换到其他线程执行，因为如果当前线程 优先级比较高的话，可能在让出锁以后，又继续获得锁，并优先执行。

# 6. 什么是线程、协程、进程
# 线程，有时被称为轻量级进程(Lightweight Process，LWP），是程序执行流的最小单元。一个标准的线程由线程ID，当前指令指针(PC），寄存器集合和堆栈组成。另外，线程是进程中的一个实体，是被系统独立调度和分派的基本单位，线程自己不独立拥有系统资源，但它可与同属一个进程的其它线程共享该进程所拥有的全部资源。一个线程可以创建和撤消另一个线程，同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约，致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件，逻辑上可以运行，在等待处理机；运行状态是指线程占有处理机正在运行；阻塞状态是指线程在等待一个事件（如某个信号量），逻辑上不可执行。每一个应用程序都至少有一个进程和一个线程。线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的被划分成一块一块的工作，称为多线程。
# 以上那一段，可以不用看！举个例子，厂家要生产某个产品，在它的生产基地建设了很多厂房，每个厂房内又有多条流水生产线。所有厂房配合将整个产品生产出来，某个厂房内的所有流水线将这个厂房负责的产品部分生产出来。每个厂房拥有自己的材料库，厂房内的生产线共享这些材料。而每一个厂家要实现生产必须拥有至少一个厂房一条生产线。那么这个厂家就是某个应用程序；每个厂房就是一个进程；每条生产线都是一个线程

# 进程，是计算机中的程序关于某数据集合上的一次运行活动，是系统进行资源分配和调度的基本单位，是操作系统结构的基础。前面的话我也没懂，用非官方的白话来解释就是——执行中的程序是进程，比如qq不是进程，但是当我们双击qq开始使用它的时候，它就变成了一个进程。我们写的python程序，只有当我们执行它的时候，它才是进程。我们正在执行的IE浏览器，QQ，pycharm都是进程，从操作系统的角度来讲，每一个进程都有它自己的内存空间，进程之间的内存是独立的。

# 协程,是在一个线程执行过程中可以在一个子程序的预定或者随机位置中断，然后转而执行别的子程序，在适当的时候再返回来接着执行。他本身是一种特殊的子程序或者称作函数。

# 7. 什么时候该用多线程、什么情况该用多进程
# 多进程的优点是稳定性好，一个子进程崩溃了，不会影响主进程以及其余进程。但是缺点是创建进程的代价非常大，因为操作系统要给每个进程分配固定的资源，
# 并且，操作系统对进程的总数会有一定的限制，若进程过多，操作系统调度都会存在问题，会造成假死状态。多线程优点是效率较高一些，但是致命的缺点是任何一
# 个线程崩溃都可能造成整个进程的崩溃，因为它们共享了进程的内存资源池。 对于任务数来说，无论是多进程或者多线程，都不能太多。因为操作系统在切换任务时，
# 会有一系列的保护现场措施，这要花费相当的系统资源，若任务过多，则大部分资源都被用做干这些了，结果就是所有任务都做不好，所以操作系统会限制进程的数量。
# 另外，考虑计算密集型及IO密集型应用程序。对于计算密集型，多任务势必造成资源浪费。对于IO密集型，因为IO速度远低于CPU计算速度，所以使用多任务方式可以大大增大程序运行效率。

# 8. 多个进程间如何通信？（注意多进程知识点回顾！！！）
# https://www.cnblogs.com/smallmars/p/7093603.html
from multiprocessing import Process #构建进程对象
import os
import time
def child_process(name):
    print('子进程%s的id为%s:' % (name,os.getpid()))
    time.sleep(5)
    print('子进程退出')

if __name__ == '__main__':
    print('主进程的id为%s' % os.getpid())
    #构建子进程
    pro = Process(target=child_process,args=('dongnaoxiaoyi',))
    pro.start()
    pro.join()

